#ifndef __MAIN_AUX__
#define __MAIN_AUX__
#include "diagfuncs.h"
#include "main_aux.h"
// ---------------------------------------------------------------------------------------------- //
// Globals ::
// ---------------------------------------------------------------------------------------------- //
double muParse_dumpAiterators(const double * args, int numargs);

double muParse_getQ(const double * args, int numargs);

double muParse_printQ(double);

double muParse_printProfile(const char *);
// -------------------------------------------------------------------------------- //
model_class & getModel(){
	static bool allocated = false;
	static model_class * QGMODEL = NULL;
        SetTypeConformer( does_type_conform_to_type );
	if(!allocated){
		QGMODEL = new model_class();
		allocated = true;
	}
	//QGMODEL->init(getsetNameListName(NULL), 1); // init the model at level 1
	return *QGMODEL;
};
// -------------------------------------------------------------------------------- //
vector<string> & getuserdefinedfuncnames(){
	static vector<string> fnames;
	return fnames;
};
// -------------------------------------------------------------------------------- //
ParserX & getParser(){ // initializes and/or returns the global ParserX
	static bool initialized = false;
	static ParserX P(pckNONE);
	if(!initialized){
		initialized = true;
		getModel().assignparserdata(P, "v", false);
		assigndiagfuncs(P, false);
	}
	return P;
}
// ------------------------------------------------------------------------------------------- //
void printMOfields(const model_class & model, FILE * fout){
	const psystem & p = model.getPsystem();
	const int numfields = static_cast<int>(p.getmeanfieldsize());
	if( numfields > 0 ){
		fprintf(fout, " %20s\t%20s\n", "field-name", "initial-expression");
	}
	for(int iter = 0; iter < numfields; iter++){
		const string & name = p.getmeanfieldname(iter);
		const string & mean_expr_init_expr = p.getmeanfieldinitexpr(iter);
		// add formatting
		fprintf(fout, " %20s\t\"%20s\"\n", name.c_str(), mean_expr_init_expr.c_str()); 
	}
};
// ------------------------------------------------------------------------------------------- //
void printMOfunctions(const model_class & model, FILE * fout){
	const psystem & p = model.getPsystem();
	const int numfuncs = p.getfunctionssize();
	if( numfuncs > 0 ){
		fprintf(fout, " %20s\t%20s\n", "field-name", "expression");
	}
	for(int iter = 0; iter < numfuncs; iter++){
		const string & name = p.getfunctionname(iter);
		const string & expr = p.getfunctionexpr(iter);
		// add formatting
		fprintf(fout, " %20s\t\"%20s\"\n", name.c_str(), expr.c_str()); 
	};
};
// ------------------------------------------------------------------------------------------- //
void printMOvariables(const model_class & model, FILE * fout){
	const psystem & p = model.getPsystem();
	const int numvars = p.getvariablesize();
	if( numvars > 0){
		fprintf(fout, " variable-name\n");
	}
	for(int iter = 0; iter < numvars; iter++){
		const string & name = p.getvariablename(iter);
		fprintf(fout, " %s\n", name.c_str());
	}
};
// ------------------------------------------------------------------------------------------- //
void printMOMacros(const model_class & model, qstream fout){
	if(fout.is_closed() )  fout = model.getSTDOUT();
	const map< string, token_list > & macro_list = model.getmacros();
	for( map< string, token_list >::const_iterator it = macro_list.begin(); it != macro_list.end(); ++it) 
	{
		const token_list & expr = (*it).second;
		fout.printf( "%s --> %s\n", (*it).first.c_str(), convert_token_list_to_string(expr).c_str() ); 
	}
}
// ------------------------------------------------------------------------------------------- //
void printMOParams(const model_class & model, qstream fout){
	if(fout.is_closed() )  fout = model.getSTDOUT();
	for(size_t f = 0; f < model.get_num_global_params(); f++){
		const parameter & param = model.get_global_param_by_index(f);
		if(param.getType() == parameter::Real){
			fout.printf("%s = %g\n", param.GetName(), param.getfValue());
		}else if(param.getType() == parameter::Integer){
			fout.printf("%s = %i\n", param.GetName(), param.getiValue());	
		}else{
			fout.printf("%s = %s\n", param.GetName(), param.getsValue());
		}
	}
}
// ------------------------------------------------------------------------------------------- //
void printMOProfiles(const model_class & model, qstream fout){
	if(fout.is_closed() )  fout = model.getSTDOUT();
	for(size_t f = 0 ; f <  model.get_num_global_profs(); f++){
		const parameter & pr = model.get_global_prof_by_index(f);
		fout.printf("Profile #%u = %s :\n", static_cast<unsigned int>(f), pr.GetName());
		if(pr.getType() == parameter::Real){
			const vector<REAL> & data = pr.getData();
			fout.printf("{");
			for(size_t x = 0; x < data.size(); x++){
				fout.printf("%g", data[x]);
				if(x < data.size() - 1){ fout.printf(","); }
			}
			fout.printf("}\n");
		}else if(pr.getType() == parameter::String){
			const vector<string> & data = pr.getsData();
			fout.printf("{");
			for(size_t x = 0; x < data.size(); x++){
				fout.printf("%s", data[x].c_str());
				if(x < data.size() - 1){ fout.printf(","); }
			}
			fout.printf("}\n");
		}else if(pr.getType() == parameter::Integer){
			const vector<int> & data = pr.getiData();
			fout.printf("{");
			for(size_t x = 0; x < data.size(); x++){
				fout.printf("%i", data[x]);
				if(x < data.size() - 1){ fout.printf(","); }
			}
			fout.printf("}\n");
		}
	}
}
// ------------------------------------------------------------------------------------------- //
double muParse_dumpAiterators(const double * args, int numargs){
	//const unsigned int numfields = getModel().get_global_param_by_name(gdb_str_NUMFIELDS).getiValue();
	const vector<rank3> & iters = getModel().getAfieldindices();
	for(size_t i = 0; i < iters.size(); i++){
		getModel().getSTDOUT().printf("\tA_{%i, %i, %i} --> %u\n",
				static_cast<int>( ( iters[i] )[0] ), 
				static_cast<int>( ( iters[i] )[1] ), 
				static_cast<int>( ( iters[i] )[2] ), 
				static_cast<unsigned int>(i));
	}
	return *(double*)nan;
}
// ------------------------------------------------------------------------------------------- //
double muParse_getQ(const double * args, int numargs){
	if(numargs != 3){
		throw ParserError("Function takes 3 arguments: (int i, int j, REAL k)");
	}
	INDEX i = (INDEX)args[0];
	INDEX j = (INDEX)args[1];
	REAL k = (REAL)args[2];
	REAL fval = computeQ_def(getModel(),k, rank2(i, j));
	return static_cast<double>(fval);
};
// ------------------------------------------------------------------------------------------- //
// Below is the mapRstaticpart callback function used by the parser to evaluate values of the static part from the command line.
// . . . . . . . . . . . . . . . . . . . . . .
double muParse_printQ(double k){
	const model_class & mo = getModel();
	//const vector<REAL> & fcoeffs = mo.get_global_prof_by_name(gdb_prof_str_FCOEFFS).getData();
	const INDEX numfields = static_cast<INDEX>(mo.get_global_param_by_name(gdb_str_NUMFIELDS).getiValue());
	mo.getSTDOUT().printf("Q[%g] = {\n", (REAL)k);
	for(INDEX i = 1; i <= static_cast<INDEX>(numfields); i++){
		mo.getSTDOUT().printf(" {");
		for(INDEX j = 1; j < static_cast<INDEX>(numfields); j++){
			mo.getSTDOUT().printf("%14g,", computeQ_def( mo, k, rank2(i, j)));
		}
		mo.getSTDOUT().printf("%14g }", computeQ_def(mo, k, rank2(i, numfields)));
		if(i < static_cast<INDEX>(numfields)) mo.getSTDOUT().printf(",");
		mo.getSTDOUT().printf("\n");
	}
	mo.getSTDOUT().printf("}\n");
	return *(double *)nan;
}
// . . . . . . . . . . . . . . . . . . . . . . 
double muParse_printProfile(const char * name){
	const model_class & mo = getModel();
	try{
		const parameter & prof  = mo.get_global_prof_by_name(name);
		if(prof.getType() == parameter::Real){
			const vector<REAL> & prof = mo.get_global_prof_by_name(name).getData();
			mo.getSTDOUT().printf(" %s = {", name);
			for(size_t i = 0; i < prof.size()-1; i++){
				mo.getSTDOUT().printf(" %g,", prof[i]);
			}
			mo.getSTDOUT().printf(" %g }\n", prof[prof.size()-1]);
			double size = (double) prof.size();
			return size;
		}else if(prof.getType() == parameter::Integer){
			const vector<int> & prof = mo.get_global_prof_by_name(name).getiData();
			mo.getSTDOUT().printf(" %s = {", name);
			for(size_t i = 0; i < prof.size()-1; i++){
				mo.getSTDOUT().printf(" %i,", prof[i]);
			}
			mo.getSTDOUT().printf(" %i }\n", prof[prof.size()-1]);
			double size = (double) prof.size();
			return size;
		}else if(prof.getType() == parameter::String){
			const vector<string> & prof = mo.get_global_prof_by_name(name).getsData();
			mo.getSTDOUT().printf(" %s = {", name);
			for(size_t i = 0; i < prof.size()-1; i++){
				mo.getSTDOUT().printf(" %s,", prof[i].c_str());
			}
			mo.getSTDOUT().printf(" %s }\n", prof[prof.size()-1].c_str());
			double size = (double) prof.size();
			return size;
		}
	}catch(cgcmexception e){
		fprintf(stderr, "Error: Specified profile '%s' does not exist.\n", name);
		throw ParserError("Specified profile does not exist.");
	}
	return 0;
};
// ---------------------------------------------------------------------------------------------
bool gnuplot_write_static_coeffs(model_class & mo, const char * fname, bool overwrite,
		const static_container * Rsc,
		const static_container * Ssc){
	fatal_stop("This function needs to be extended to write out the S static weights too.");
	//bool gnuplot_write_static_coeffs(model_class & mo, const char * fname, bool overwrite, qgcomplex * Rs, qgcomplex * Ss,	
	//rank11 * index_map, const unsigned int & map_size){
	const qgcomplex * Rs = Rsc->getcoefficients();
	const rank11 * Rmap = Rsc->get_index_map();
	const qgcomplex * Ss = Ssc->getcoefficients();
	int Rmapsize = Rsc->get_size();
	if( ! overwrite ){
		FILE * gout = fopen( fname, "r" );
		if(gout != NULL ){ // file exists
			fclose(gout); return false; 
		}
	}
	FILE * gout = fopen( fname, "w" );
	const vector<REAL> & kdomain_centered = mo.get_global_prof_by_name(gdb_prof_str_K_DOMAIN_CENTERED).getData();
	const char * labels[8] = { "mki", "nki", "i", "j", "k", "m", "a", "b" };
	for(int k = 0; k < 8; k++){
		fprintf(gout, "%5s ", labels[k] );
	}
	fprintf(gout, "%10s  %10s  %10s  ", "k", "p", "q");
	fprintf(gout,"%15s  %15s\n", "Rs_real", "Rs_imag");
	for(int i = 0; i < Rmapsize; i++){
		const rank11 & r = Rmap[i];
		//int indices [8] = { r.mk, r.nk, r.i, r.j, r.k, r.m, r.a, r.b };
		int indices [8] = { r[0], r[1], r[5], r[6], r[7], r[8], r[9], r[10] };
		for(int k = 0; k < 8; k++){
			fprintf(gout, "%5i ", indices[k] );
		}
		fprintf(gout, "%10g  %10g  %10g  ",  kdomain_centered[r[2]], kdomain_centered[r[3]], kdomain_centered[r[4]] );
		fprintf(gout, "%15g  %15g\n", Rs[i].real(), Rs[i].imag() );
	} 
	fclose(gout); 
	return true;
}
#endif
